#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/udp.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include "speech_recognizer.h"
#include "../../include/qisr.h"
#include "../../include/msp_cmn.h"
#include "../../include/msp_errors.h"
#define FRAME_LEN 640
#define BUFFER_SIZE 4096

#define BUF_SIZE 1024 // 接收缓冲区的大小

// char recvbuf[BUF_SIZE]; // 接收缓冲区
int udp_socket; // UDP套接字的文件描述符

#define MYPORT 8888 // 本主机的端口

struct sockaddr_in src = {0}; // 用于记录发送方主机的信息(端口和IP地址)
socklen_t srclen = sizeof(src);

static char *g_result = NULL;
static unsigned int g_buffersize = BUFFER_SIZE;

typedef struct btn_status
{
	int is_press;
	int is_click;
	int is_longpress;
	int btn_index;
} btn_status_t;
btn_status_t btn_status = {0, 0, 0, 0};
int online_flag = 0; // 作为客户端上线标志

// 显示识别结果
static void show_result(char *string, char is_over)
{
	printf("\r识别结果: [ %s ]", string);
	if (is_over)
		putchar('\n');
}

// 识别结果回调函数
void on_result(const char *result, char is_last)
{
	if (result)
	{
		size_t left = g_buffersize - 1 - strlen(g_result);
		size_t size = strlen(result);
		if (left < size)
		{
			g_result = (char *)realloc(g_result, g_buffersize + BUFFER_SIZE);
			if (g_result)
				g_buffersize += BUFFER_SIZE;
			else
			{
				printf("内存分配失败\n");
				return;
			}
		}
		strncat(g_result, result, size);
		show_result(g_result, is_last);
	}
}

// 语音开始回调
void on_speech_begin()
{
	if (g_result)
	{
		free(g_result);
	}
	g_result = (char *)malloc(BUFFER_SIZE);
	g_buffersize = BUFFER_SIZE;
	memset(g_result, 0, g_buffersize);

	printf("开始监听...\n");
}

// 语音结束回调
void on_speech_end(int reason)
{
	if (reason == END_REASON_VAD_DETECT)
		printf("\n说话完成\n");
	else
		printf("\n识别器错误 %d\n", reason);
}

// 使用麦克风进行识别，最多监听 15 秒
static void demo_mic(const char *session_begin_params)
{
	int errcode;
	int i = 0;

	struct speech_rec iat;
	struct speech_rec_notifier recnotifier = {
		on_result,
		on_speech_begin,
		on_speech_end};

	errcode = sr_init(&iat, session_begin_params, SR_MIC, &recnotifier);
	if (errcode)
	{
		printf("语音识别初始化失败\n");
		return;
	}

	errcode = sr_start_listening(&iat);
	if (errcode)
	{
		printf("开始监听失败 %d\n", errcode);
	}

	// 演示：录制 10 秒钟
	while (i++ < 10)
		sleep(1);

	errcode = sr_stop_listening(&iat);
	if (errcode)
	{
		printf("停止监听失败 %d\n", errcode);
	}

	sr_uninit(&iat);
}

// 封装的函数：开始语音识别并输出识别结果
int recognize_speech_from_mic()
{
	int ret = MSP_SUCCESS;
	/* 登录参数, 不能随便改动 */
	const char *login_params = "appid = 548fd476, work_dir = .";

	const char *session_begin_params =
		"sub = iat, domain = iat, language = zh_cn, "
		"accent = cantonese, sample_rate = 16000, "
		"result_type = plain, result_encoding = utf8";

	// 登录
	ret = MSPLogin(NULL, NULL, login_params);
	if (MSP_SUCCESS != ret)
	{
		printf("MSPLogin 失败，错误码 %d。\n", ret);
		return ret;
	}

	printf("开始从麦克风进行语音识别...\n");

	demo_mic(session_begin_params); // 调用麦克风识别函数

	MSPLogout(); // 登出...

	return ret;
}

// 接收线程的任务
void *recv_handler(void *arg)
{
	for (;;)
	{
		// 接收数据，未收到数据时会阻塞
		recvfrom(udp_socket, &btn_status, sizeof(btn_status_t), 0, (struct sockaddr *)&src, &srclen);

		online_flag = 1; // 表示客户端上线

		// 输出数据
		// printf("recv data from [%s] is [%d]\n", inet_ntoa(src.sin_addr), btn_status.is_longpress);
	}
}

void *send_handler(void *arg)
{

	// char sendbuf[BUF_SIZE] = {0}; // 用于存储待发送的数据

	// 1.创建UDP套接字
	udp_socket = socket(AF_INET, SOCK_DGRAM, 0);
	if (-1 == udp_socket)
	{
		fprintf(stderr, "create udp socket error,%s\n", strerror(errno));
		exit(-1);
	}

	// 2.bind()是可选项，如果客户端需要接收来自其他主机的消息，则应该调用
	struct sockaddr_in myaddr = {0};
	myaddr.sin_family = AF_INET;		 // IPV4
	myaddr.sin_port = htons(MYPORT);	 // 本地端口，需要转换为网络字节序
	myaddr.sin_addr.s_addr = INADDR_ANY; // 本地地址，需要转换为网络字节序
	bind(udp_socket, (struct sockaddr *)&myaddr, sizeof(myaddr));

	// 4.死循环
	for (;;)
	{
		// 服务器先运行，当服务器收到客户端的消息后，才可以给客户端回消息
		if (online_flag)
		{
			// 发送数据

			if (g_result)
			{
				if (strlen(g_result) != 0)
				{
					sendto(udp_socket, g_result, strlen(g_result), 0, (struct sockaddr *)&src, sizeof(src));
					printf("send ok:\n");
					sleep(3);
					memset(g_result, 0, BUFFER_SIZE);
				}
			}
		}
	}

	// 5.关闭套接字
	close(udp_socket);

	return 0;
}

// 主函数
int main(int argc, char *argv[])
{

	// 3.创建一条接收线程，专门用于接收其他主机的消息
	pthread_t recv_thread;
	pthread_create(&recv_thread, NULL, recv_handler, NULL);
	pthread_t send_thread;
	pthread_create(&send_thread, NULL, send_handler, NULL);

	while (1)
	{
		if (btn_status.is_longpress == 1)
		{
			recognize_speech_from_mic(); // 调用语音识别并输出结果
		}
	}

	return 0;
}
